#include "sudokuform.h"
#include "ninesquaregrid.h"
#include "sudokucell.h"
#include "sudokucellsignals.h"
#include <QPainter>
#include <QDebug>

namespace sudoku_ui {
    SudokuForm::SudokuForm(QWidget *parent)
        : QWidget{parent}
    {
        for (auto i = 1; i <= 9; i++)
        {
            this->m_board.insert(std::make_pair(i, new NineSquareGrid(i, this)));
            this->m_cellmap.insert(std::make_pair(i, std::vector<SudokuCell*>()));
        }

        connect(SudokuCellSignals::globalInstance(),
                &SudokuCellSignals::selectedCell,
                this,
                &SudokuForm::onSelectedCell);

        connect(SudokuCellSignals::globalInstance(),
                &SudokuCellSignals::selectedNineGrid,
                this,
                &SudokuForm::onSelectedNineGrid);

        connect(SudokuCellSignals::globalInstance(),
                &SudokuCellSignals::check,
                this,
                &SudokuForm::onCheck);

        connect(SudokuCellSignals::globalInstance(),
                &SudokuCellSignals::appendCell,
                this,
                &SudokuForm::onAppendCell);

        connect(SudokuCellSignals::globalInstance(),
                &SudokuCellSignals::removeCell,
                this,
                &SudokuForm::onRemoveCell);
    }

    SudokuForm::~SudokuForm()
    {
        for (auto & grid : this->m_board)
        {
            grid.second->deleteLater();
        }
        this->m_board.clear();
    }

    void SudokuForm::setNumber(int num)
    {
        this->m_selectedCell->setValue(num);
    }

    void SudokuForm::setNumber(bool state, int num)
    {
        if (this->m_selectedCell == nullptr || this->m_selectedGrid == nullptr)
            return;

        if (state)
            this->m_selectedCell->setValue(num);
        else
            this->m_selectedCell->updateValue(num);
    }

    void SudokuForm::setNumbers(const std::vector<std::vector<int> > &nums)
    {
        // 初始化各数独单元格
        for (auto i = 0; i < 9; i++)
        {
            for (auto j = 0; j < 9; j++)
            {
                auto gridIndex = toGridIndex(i, j);
                auto cellIndex = toCellIndex(i, j);
                this->m_board[gridIndex + 1]->setValue(cellIndex, nums[i][j]);
            }
        }
    }

    std::vector<std::vector<int> > SudokuForm::getNumbers()
    {
        auto result = std::vector<std::vector<int>>(9, std::vector<int>(9, 0));
        for (auto i = 0; i < 9; i++)
        {
            for (auto j = 0; j < 9; j++)
            {
                auto cell = this->m_board[toGridIndex(i, j) + 1]->cell(toCellIndex(i, j) + 1);
                if (cell != nullptr && cell != this->m_selectedCell)
                    result[i][j] = cell->value();
            }
        }
        return result;
    }

    bool SudokuForm::isOk()
    {
        auto ok = true;
        for (const auto& cells : this->m_cellmap)
        {
            if (cells.second.size() < 9)
            {
                ok = false;
                break;
            }
        }
        return ok;
    }

    int SudokuForm::remainCount(int num)
    {
        return 9 - this->m_cellmap[num].size();
    }

    void SudokuForm::newGame()
    {
        this->m_cellmap.clear();
        for (auto i = 1; i <= 9; i++)
        {
            this->m_cellmap[i].clear();
            this->m_board[i]->newGame();
        }
        this->m_selectedCell = nullptr;
        this->m_selectedGrid = nullptr;
        SudokuCellSignals::selectedSudokuCell(nullptr);
    }

    std::vector<std::pair<int, int> > SudokuForm::checkGame()
    {
        auto result = std::vector<std::pair<int, int>>();

        auto cells = this->getNumbers();

        // 检查行
        for (auto row = 0; row < 9; row++)
        {
            std::map<int, int> r;
            for (auto col = 0; col < 9; col++)
            {
                if (r.count(cells[row][col]) == 0)
                    r[cells[row][col]] = col;
                else
                {
                    result.push_back(std::make_pair(row, col));
                }
            }
        }

        // 检查列
        for (auto col = 0; col < 9; col++)
        {
            std::map<int, int> r;
            for (auto row = 0; row < 9; row++)
            {
                if (r.count(cells[row][col]) == 0)
                    r[cells[row][col]] = row;
                else
                    result.push_back(std::make_pair(row, col));
            }
        }

        // 检查九宫格
        for (auto i = 0; i < 9; i++)
        {
            std::map<int, int> r;
            for (auto j = 0; j < 9; j++)
            {
                auto row = i / 3 * 3 + j / 3;
                auto col = i % 3 * 3 + j % 3;
                if (r.count(cells[row][col]) == 0)
                    r[cells[row][col]] = j;
                else
                    result.push_back(std::make_pair(row, col));
            }
        }

        return result;
    }

    int SudokuForm::toGridIndex(int row, int col)
    {
        return (row / 3) * 3 + col / 3;
    }

    int SudokuForm::toCellIndex(int row, int col)
    {
        auto r = row / 3;
        auto c = col / 3;
        return (row - r * 3) * 3 + (col - c * 3);
    }

    void SudokuForm::onSelectedCell(SudokuCell *cell)
    {
        if (this->m_selectedCell != nullptr)
        {
            // 清除
            auto num = m_selectedCell->value();
            if (num > 0 && num < 10)
            {
                auto &cells = this->m_cellmap[num];
                for (auto& cell : cells)
                    cell->setSame();
            }
        }

        this->m_selectedCell = cell;

        if (this->m_selectedCell != nullptr)
        {
            auto num = m_selectedCell->value();
            if (num > 0 && num < 10)
            {
                auto &cells = this->m_cellmap[num];
                for (auto& cell : cells)
                    cell->setSame(true);
            }
        }
    }

    void SudokuForm::onSelectedNineGrid(NineSquareGrid *grid)
    {
        this->m_selectedGrid = grid;
    }

    void SudokuForm::onCheck()
    {
        if (this->m_selectedGrid == nullptr || this->m_selectedCell == nullptr)
            return;

        auto gridIndex = this->m_selectedGrid->index();
        auto cellIndex = this->m_selectedCell->index();

        qDebug() << tr("单元格(%1, %2)").arg(gridIndex).arg(cellIndex);
        // qDebug() << tr("检查");

        SudokuCellSignals::checkGame((gridIndex - 1) / 3 * 3 + (cellIndex - 1) / 3,
                                     (gridIndex - 1) % 3 * 3 + (cellIndex - 1) % 3,
                                     this->m_selectedCell->value());
    }

    void SudokuForm::onAppendCell(int num, SudokuCell *cell)
    {
        if (num < 1 || num > 9)
            return;
        auto& cells = this->m_cellmap[num];
        auto state = false;
        for (auto it = cells.begin(); it != cells.end(); it++)
        {
            if (*it == cell)
            {
                state = true;
                break;
            }
        }
        if (state == false)
            cells.push_back(cell);
    }

    void SudokuForm::onRemoveCell(int num, SudokuCell *cell)
    {
        if (num < 1 || num > 9)
            return;

        auto& cells = this->m_cellmap[num];
        for (auto it = cells.begin(); it != cells.end(); it++)
        {
            if (*it == cell)
            {
                cells.erase(it);
                break;
            }
        }
    }

    void SudokuForm::check()
    {
        // 检查
    }

    void SudokuForm::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event)
        QWidget::paintEvent(event);
    }

    void SudokuForm::resizeEvent(QResizeEvent *event)
    {
        Q_UNUSED(event)

        auto w = this->width() / 3;
        auto h = this->height() / 3;

        w = h = std::min(w, h);
        auto x = (this->width() - w * 3) / 2;
        auto y = (this->height() - h * 3) / 2;

        for (auto i = 1; i <= 9; i++)
        {
            this->m_board[i]->setGeometry(x, y, w - 2, h - 2);
            x += w;
            if (x >= 3 * w)
            {
                x = (this->width() - w * 3) / 2;
                y += h;
            }
        }
    }


}


